//-----------------------------------------------------------------------

// <copyright file="AutoFormPanel.cs" company="Jamie Knight">

// Copyright (c) Jamie Knight. All rights reserved.

// </copyright>

//-----------------------------------------------------------------------

using DevKnightlie.WPF.AutoForm.FieldDefinitions;
using DevKnightlie.WPF.AutoForm.Interfaces;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;

namespace DevKnightlie.WPF.AutoForm
{

    /// <summary>
    /// Builds input forms from a list of FieldDefinition objects
    /// </summary>
    public class AutoFormPanel : System.Windows.Controls.ContentControl
    {
        #region Private Variables

        private FieldDefinitionList _fieldDefinitions = new FieldDefinitionList();
        private Dictionary<BaseFieldDefinition, Control> _fieldControls = new Dictionary<BaseFieldDefinition, Control>();
        private Dictionary<BaseFieldDefinition, Control> _fieldLabels = new Dictionary<BaseFieldDefinition, Control>();
        private Dictionary<BaseFieldDefinition, Control> _containerControls = new Dictionary<BaseFieldDefinition,Control>();
        private WillAddFieldLabelEvent _willAddFieldLabel;
        private IModelObjectFormDefiner _modelObjectFormDefiner;
        private ILayoutStrategy _layoutStrategy;
        bool _controlsCreated = false;

        #endregion


        #region Properties



        /// <summary>
        /// Returns the list of FieldDefinition objects used to build the form
        /// </summary>
        public FieldDefinitionList FieldDefinitions
        {
            get { return _fieldDefinitions; }
            set
            {
                _fieldDefinitions = value;
            }
        }

        /// <summary>
        /// Returns the layout strategy object being used to arrange field controls the form
        /// </summary>
        public ILayoutStrategy LayoutStrategy
        {
            get { return _layoutStrategy; }
            set
            {
                if (value != _layoutStrategy)
                {
                    if (value != null && value.FormPanel != null && value.FormPanel != this)
                    {
                        throw new ArgumentException("Layout strategy is already connected to an AutoFormPanel");
                    }
                    _layoutStrategy = value;
                    if (value != null)
                    {
                        value.FormPanel = this;
                    }
                }
            }
        }


        /// <summary>
        /// Holds a reference to a ModelObjectFormDefiner object, which scan an object and configures input fields for its writeable properties
        /// </summary>
        public IModelObjectFormDefiner ModelObjectFormDefiner
        {
            get
            {
                return _modelObjectFormDefiner;
            }
            set
            {
                _modelObjectFormDefiner = value;
                if (value!=null && value.ModelObject != null)
                {
                    FieldDefinitions.Clear();
                    FieldDefinitions.AddRange(value.FieldDefinitionList());
                }
            }
        }



        #endregion


        #region Events


        public delegate void WillProcessFieldDefinitionEvent(object sender, FieldDefinitionEventArgs e);

        private WillProcessFieldDefinitionEvent _willProcessFieldDefinition;

        /// <summary>
        /// Fired when the given FieldDefinition object is about to be procesed
        /// </summary>
        public event WillProcessFieldDefinitionEvent WillProcessFieldDefinition
        {
            add { _willProcessFieldDefinition += value; }
            remove { _willProcessFieldDefinition -= value; }
        }

        public delegate void WillAddInputControlEvent(InputControlEventArgs args);

        private WillAddInputControlEvent _willAddInputControl;

        /// <summary>
        /// Fired when the component is about to add an input control to the form
        /// </summary>
        public event WillAddInputControlEvent WillAddInputControl
        {
            add { _willAddInputControl += value; }
            remove { _willAddInputControl -= value; }
        }



        public delegate void WillAddFieldLabelEvent(InputControlEventArgs args);


        /// <summary>
        /// Fired when the component is about to add a field label to the form
        /// </summary>
        public event WillAddFieldLabelEvent WillAddFieldLabel
        {
            add { _willAddFieldLabel += value; }
            remove { _willAddFieldLabel -= value; }
        }

        #endregion


        #region Constructor


        public AutoFormPanel(
            ILayoutStrategy layoutStrategy)
        {
            LayoutStrategy = layoutStrategy;
            Loaded += AutoFormPanel_Loaded;
            Unloaded += AutoFormPanel_Unloaded;
            Initialized += AutoFormPanel_Initialized;
        }

        void AutoFormPanel_Initialized(object sender, EventArgs e)
        {

        }


        /// <summary>
        /// Reacts to the AutoFormPanel being unloaded
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <remarks>When the AutoForm is unloaded (say from a TabControl whose selection has changed), we should remove the input controls and fields from their parent</remarks>
        void AutoFormPanel_Unloaded(object sender, RoutedEventArgs e)
        {
            if (LayoutStrategy != null)
            {
                LayoutStrategy.DropLayout();
            }
            foreach (BaseFieldDefinition fieldDefinition in FieldDefinitions)
            {
                fieldDefinition.Reset();
            }
            _containerControls.Clear();
            _fieldControls.Clear();
            _fieldLabels.Clear();
            _controlsCreated = false;
        }

        void AutoFormPanel_Loaded(object sender, RoutedEventArgs e)
        {
            if (_controlsCreated)
            {
                return;
            }
            if (LayoutStrategy == null)
            {
                // Create a default layout strategy if none has been defined
                LayoutStrategy = new GridLayoutStrategy();
            }
            RefreshForm();
        }



        public AutoFormPanel()
            : this(null) // new GridLayoutStrategy())
        {

        }


        #endregion



        /// <summary>
        /// Recreate the input form from the current configuration
        /// </summary>
        public void RefreshForm()
        {
            if (!_controlsCreated || (_fieldDefinitions != null && _fieldDefinitions.Count > 0))
            {
                CreateInputControls();
                LayoutStrategy.CreateLayout();
            }
        }




        /// <summary>
        /// Obtain input controls and field labels from configured field definitions
        /// </summary>
        private void CreateInputControls()
        {
            if (_controlsCreated)
            {
                return;
            }
            if (LayoutStrategy == null)
            {
                throw new InvalidOperationException("Cannot create controls without a Layout Strategy in place");
            }
            _fieldControls.Clear();
            _fieldLabels.Clear();

            foreach (BaseFieldDefinition fieldDefinition in FieldDefinitions)
            {
                if (_willProcessFieldDefinition != null)
                {
                    _willProcessFieldDefinition(this, new FieldDefinitionEventArgs(fieldDefinition));
                }
                Control newControl = fieldDefinition.ContainerControl;
                if (newControl == null) continue;
                _fieldControls.Add(fieldDefinition, newControl);
                _containerControls.Add(fieldDefinition, fieldDefinition.ContainerControl);
                Control fieldLabel = fieldDefinition.LabelControl;
                if (fieldLabel != null)
                {
                    _fieldLabels[fieldDefinition] = fieldLabel;
                }
            }
            _controlsCreated = true;
            LayoutStrategy.Initialise(_containerControls, _fieldLabels);
        }


        public override string ToString()
        {
            return String.Format("AutoFormPanel: {0}, Strategy:{1}, Controls created:{2}",
                Name, LayoutStrategy, _controlsCreated ? "Yes" : "No");
        }


        

    }
}
